package graph_viewer.canvas;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

import java.io.*;
import java.util.*;

import graph_viewer.animator.*;
import graph_viewer.app_exception.*;
import graph_viewer.experiment.*;
import graph_viewer.factory.*;
import graph_viewer.drawer.*;
import graph_viewer.structure.*;
import graph_viewer.util.*;
import graph_viewer.layout.*;
import graph_viewer.layout.layout_adjuster.*;
import graph_viewer.gui.*;

public class Graph_Canvas extends Drawer_Canvas{// implements Runnable{
	static final private String 
		DEFAULT_GRAPH_TYPE	= Visual_Component.CLUSTERED_GRAPH,
		DEFAULT_LAYOUT		= Layout.DEFAULT_LAYOUT,	
		DEFAULT_ADJUSTER	= Cluttered_Nodes_Adjuster.DEFAULT_ADJUSTER;
		
	static final private int DEFAULT_SFV_ORDER	 = 1;	


	// canvas enviroment variables
	//private String		view_status;
	private ArrayList	focus_history;
	private Canvas_Metrics	
					the_canvas_metrics;

	//component attributes
	private Graph		the_graph;

	private String		graph_type;
	private String		data_file_name;
	
	//@since 8/4/2004 experiment
	Layout_Adjuster	the_layout_adjuster;
	Layout			the_layout_alg;

	private JComponent	animation_board;


	private Experiment_Metrics 
					the_base_experiment_metrics,
					the_target_experiment_metrics;
	
	//@since 8/4/2004
	private HashMap	initial_position_table;
	
	//@since 9/1/2004
	private double		the_initial_aspect_ratio;

	//@since 9/3/2004
	private Abridgement	the_abridgement;

	//@since 9/6/2004
	private String focus_trace_file;
	private boolean is_doing_experiment;			
	final int 
		EXP_TIMES			= 1,
		INTERACTION_TIMES	= 35;
	

	boolean IS_NODE_POSITIONS_SET	= false;
	private boolean IS_RANDOM_NODE	= true;

	private boolean	IS_CLUSTERING		= true;
										//false;
												
	private boolean IS_ANIMATION = 	true;
									//false;
	private boolean	HAS_IMPORTANT_EDGE	=	true;
												//false;

	//============================================================================
	// construtors
	//============================================================================
	public Graph_Canvas(){
		initialize_canvas();	 
	}

	public Graph_Canvas( String graph_type, String file_name ) throws IOException{
		this();
		
		set_graph_type( graph_type);
		set_data_file_name( file_name );
		
		initialize_graph();
	}

	public Graph_Canvas( String file_name ) throws IOException{
		this( DEFAULT_GRAPH_TYPE, file_name );
	}

	//@since 2/07/2004. animation
	public Graph_Canvas( String a_graph_type, String a_file_name, JComponent a_animation_board ) throws IOException{
		this( a_graph_type, a_file_name );

		set_animation_board( a_animation_board );
	}
	
	//@since 2/07/2004. animation
	public Graph_Canvas( String file_name, JComponent a_animation_board ) throws IOException{
		this( file_name );
		
		animation_board = a_animation_board;
	}	
	//============================================================================
	// public  methods
	//============================================================================
	int image_flag	= 0;
	public void paint(final Graphics g) {
		draw_graph( g );	

		if ( image_flag%5==0){
			generate_image();
		}
		image_flag++;
	}
	
	public void mousePressed(final MouseEvent e ){
		
		Node	clicked_node	= null;

		ArrayList nodes	= the_graph.find_nodes();
		
		for ( Iterator _i = nodes.iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();

			if ( node.is_leaf_cluster() ){
				if ( has_pick_node( node, e ) ){				
					clicked_node = node;
					break;
				}				
			}
		}

		if ( clicked_node !=null ){//pick a node
			mouse_press_action( clicked_node );
		}
	}
	

	//set the canvas to initial view
	public void reset(){		
		Out.println( "\n<Reset Graph>\n" );
		initialize_canvas();
		threshold_nodes();
		
		draw_initial_view();
		
		restore_initial_position();

		repaint();
		Debug.println( "After reset: restore_initial_position: " + get_initial_position_table(), 47 );
	}
	


	public void draw_initial_view(){	
		open_root();

		the_abridgement	= new Abridgement( get_graph() );
		Debug.println( "After draw_initial_view, the_abridgement is: " + the_abridgement, 41 );		
	}

	public void mouse_press_action( Node a_node ){
		if ( a_node.is_leaf() ){
			return ;// a leaf node won't be opened.
		}
		
		Debug.println( "===================GRAPH_CANVAS.open_focus: " + a_node, 48 );

		Debug.println( "\tFocus: " + a_node, 48 );		
		add_focus( a_node );				
		
		if ( IS_CLUSTERING){
			Debug.println( "\tGRAPH_CANVAS.distort_leaf_clusters", 48 );		
			distort_leaf_clusters( the_graph, the_canvas_metrics.get_focus() );
			Debug.println( "\tGRAPH_CANVAS.distort_leaf_clusters Finished!\n", 48 );			
			
			generate_image();

			Debug.println( "\tGRAPH_CANVAS.do_clustering", 48 );
			do_clustering();
			Debug.println( "\tGRAPH_CANVAS.do_clustering Finished!\n", 48 );
			generate_image();
		}
		
		Debug.println( "===================GRAPH_CANVAS.open_focus action: " + a_node, 48 );
		open_focus();		
		generate_image();
		Debug.println( "===================GRAPH_CANVAS.open_focus Finished! "+ a_node, 48 );	
		

		repaint();	

		
	}
	
	public void adjust_layout( Node a_cluster ){
		the_layout_adjuster.do_adjust_action( a_cluster, this );
	}

	public void generate_image(){
		Node_Position	 screen	= GUI_Util.find_screen_boundary();
		int	
			screen_height		= screen.get_height(),
			screen_width		= screen.get_width();

		Node_Position	 root_position = the_graph.find_tree().get_root().get_position();
		int
			root_height	= root_position.get_height(),
			root_width	= root_position.get_width();

		Debug.println( "\troot position is: " + root_position, 46);
		
		try{
			Node_Position_Helper.constraint_root_to_screen_left_top( the_graph );

			BufferedImage bi = new BufferedImage( Math.max( screen_width, root_width ) + 10, Math.max( screen_height, root_height ) + 10 , BufferedImage.TYPE_4BYTE_ABGR_PRE);
		
			Graphics2D	_2g	= bi.createGraphics();
			
			draw_graph( _2g );		

			String image_name	 = //"" + System.currentTimeMillis() + ".png"; //
								File_Util.get_file_name( get_data_file_name() ) +"_" + the_layout_adjuster.get_name()+ System.currentTimeMillis() +".png";

			Out.println( "Generate image_file: " + image_name );
			File file = new File( image_name );

			javax.imageio.ImageIO.write(bi, "PNG", file);
		}
		catch (Exception e){
		}
	}

	public void do_static_experiment(){
		do_experiment_static_imple();
	}

	public void do_experiment_static_imple(){
		String[] adjusters	= Cluttered_Nodes_Adjuster.ADJUSTER_VARIANTS;
		String experiment_reuslt_file = Experiment.dir 
							//+ File_Util.get_file_name( get_data_file_name() ) 
							//+"_" + the_layout_adjuster.get_name() 							
							+ "data_export.exp";
		try{
			File_Util.append_to_file( experiment_reuslt_file, "============================================" );	
		}catch( IOException ioe ) { }


		for ( int i = 0; i < adjusters.length; i++ ){
			Debug.println( "Do experiment of "  + adjusters[i] , 46 );	
			reset();

			String an_adjuster	= adjusters[i];
			
			set_layout_adjuster( new Layout_Factory().create_adjuster( an_adjuster ) );
			open_root();				
			
			try{
				Experiment_Metrics em = new Experiment_Metrics( this, the_abridgement );
				String experiment_result	 = em.to_static_string();

				Out.println( experiment_result );	

				Debug.println( "experiment_reuslt_file"  + experiment_result , 46 );	
				File_Util.append_to_file( experiment_reuslt_file, experiment_result );	
				
				generate_image();
			}catch (IOException ioe){}	
			
			Debug.println( "Do experiment of "  + adjusters[i] , 46 );	
		}	

		Out.println( "do_static_experiment() Finish!" );
	}

	public void do_dynamic_experiment(){
		for ( int i = 0; i <EXP_TIMES ; i++ ){
			is_doing_experiment			= true;
			Out.println( "the " + i + " time exp." );
			Debug.println( "generate_focus_trace", 57 );
		
			generate_focus_trace();		

			ArrayList focus_trace	= File_Util.read_file( this.focus_trace_file );

			Debug.println( "do_experiment_dynamic_impl", 57 );
			do_experiment_dynamic_impl( focus_trace );

			reset();
		}	
	}

	public void do_visual_complexity_experiment(){
		Debug.println( "Do_visual_complexity_experiment!", 54 );
		
		Debug.println( "Generate focud trace!", 54 );
		generate_focus_trace();

		reset();

		Debug.println( "calculate visual complexity!", 54 );
		ArrayList focus_trace	= File_Util.read_file( this.focus_trace_file );

		String experiment_reuslt_file = Experiment.dir + "visual.exp";
		
		File_Util.delete_file( experiment_reuslt_file );
		for ( Iterator _i = focus_trace.iterator(); _i.hasNext(); ){
			String an_focus	= (String)_i.next();
			
			if ( an_focus.length() > 0 ){
				Debug.println( "do_experiment: " + an_focus, 45 );
				mouse_press_action( the_graph.search(an_focus ) );

				int visual_compexity	= the_graph.find_displayed_nodes().size();
				try{
					File_Util.append_to_file( experiment_reuslt_file, an_focus + ", " + visual_compexity + "" );						
				}
				catch ( IOException ioe){
				}
			}
		}

		reset();
		
		Debug.println( "Do_no filitering experiment!", 54 );
		try{
			File_Util.append_to_file( experiment_reuslt_file,  "\n" );						
		}
		catch ( IOException ioe){
		}

		for ( Iterator _i = focus_trace.iterator(); _i.hasNext(); ){
			String an_focus	= (String)_i.next();
			
			if ( an_focus.length() > 0 ){
				Debug.println( "do_experiment: " + an_focus, 45 );
				
				Node experiment_focus = ( the_graph.search(an_focus ) );
				
				if (! experiment_focus.is_leaf_cluster() ){
					continue;
				}
				
				
				new Layout_Actor( this ).open_node( experiment_focus );
				int visual_compexity	= the_graph.find_displayed_nodes().size();
				try{
					File_Util.append_to_file( experiment_reuslt_file, an_focus + ", " + visual_compexity + "" );						
				}
				catch ( IOException ioe){
				}
			}
		}

		
		reset();
		Debug.println( "Do_visual_complexity_experiment Finish!", 54 );
	}
	
	//============================================================================
	// setter & getter
	//============================================================================

	//set canvas enviroment variables//==============
	public void set_data_file_name( String file_name ){
		this.data_file_name	= file_name;
	}

	public String get_data_file_name(){
		return data_file_name;
	}
	
	public void set_graph_type( String graph_type ) {
		this.graph_type	= graph_type;
	}

	public String get_graph_type(){
		return this.graph_type;
	}

	//=============end set canvas enviroment variables===============	
	public void set_graph( Graph a_graph ){
		the_graph	= a_graph;
	}

	public Graph get_graph(){
		return this.the_graph;
	}
	
	public void  set_canvas_metrics( Canvas_Metrics a_canvas_metrics ){
		this.the_canvas_metrics = a_canvas_metrics;
	}

	public Canvas_Metrics  get_canvas_metrics( ){
		return the_canvas_metrics;
	}

	public void set_animation_board( JComponent an_animation_board ){
		this.animation_board	 = an_animation_board;
	}

	public JComponent get_animation_board(){
		return this.animation_board;
	}

	public void set_layout( Layout a_layout_alg ){
		this.the_layout_alg	= a_layout_alg;
	}

	public Layout get_layout(){
		return this.the_layout_alg;
	}
	
	public void set_layout_adjuster( Layout_Adjuster a_layout_adjuster ){
		this.the_layout_adjuster = a_layout_adjuster;
	}

	public Layout_Adjuster get_layout_adjuster(){
		return this.the_layout_adjuster;
	}

	public Experiment_Metrics get_base_experiment_metrics(){
		return this.the_base_experiment_metrics;
	}

	public Experiment_Metrics get_target_experiment_metrics(){
		return this.the_target_experiment_metrics;
	}

	public void set_target_experiment_metrics( Experiment_Metrics em ){
		this.the_target_experiment_metrics	= em;
	}

	public void set_initial_position_table( HashMap a_initial_position_table ){
		this.initial_position_table = a_initial_position_table;
	}

	public HashMap get_initial_position_table(){
		return this.initial_position_table;
	}


	public boolean get_is_positions_set(){
		return this.IS_NODE_POSITIONS_SET;
	}
	//============================================================================
	// private methods
	//============================================================================
	private void initialize_canvas(){				
		addMouseListener(this);
		addMouseMotionListener(this);

		focus_history = new ArrayList();					
		the_canvas_metrics	= new Canvas_Metrics();

		File_Util.delete_file( File_Util.log );
		File_Util.delete_file( Experiment.focus_trace );
		
		the_layout_alg	= new Layout_Factory().create_layout( DEFAULT_LAYOUT );		
		the_layout_adjuster	 = new Layout_Factory().create_adjuster( DEFAULT_ADJUSTER );
	}
		
	//pre: graph_type has already been set
	private void initialize_graph(){
		Graph_Factory	gf	= new Graph_Factory( );
		
		//Debug.println( "Building Orignal graph...", 2 );
		the_graph	= gf.create( data_file_name, this.graph_type );		
		Node_Position_Helper.initialize_root_position( the_graph );
		
		set_node_position();
		
		threshold_nodes();		
	}
	

	/** 
	 * only setting random postions is implemented
	 * setting other types of positions need to be implemented
	 */
	private void set_node_position(){
		if ( IS_RANDOM_NODE ){
			set_random_position();
			IS_NODE_POSITIONS_SET = true;
		}else{
			set_customizd_position();
		}
		
		create_initial_position_table();
	}
	
	/** To be implemented */
	private void set_customizd_position(){
		//do nothing
		/** to implement */		
		/* 
		 * do node iteration as set_random_node();
		 * for each node n, n.set_position(Node_Postion np);
		 */
	}
	
	private void set_random_position(){
		for ( Iterator _i = the_graph.find_nodes().iterator(); _i.hasNext(); ){
			Node a_node	= (Node)_i.next();

			if (! a_node.is_root() ){
				/** change the following line to set other specified postions */
				/** Note that only rectangle is supported as the shape of node by this program */
				a_node.set_position( Node_Position_Helper.poistion_randomizer() );
			}
		}
	}
	
	private void open_root(){
		Debug.println( "Open root of " + the_graph,10 );
		
		if ( !IS_NODE_POSITIONS_SET ){			
			initialize_graph();
		}

		ArrayList nodes	= the_graph.find_nodes();

		Node root	= the_graph.find_tree().get_root();
		
		add_focus( root );	
		
		new Layout_Actor( this ).open_node( root );
		
		Debug.println( "\n======================GRAPH_CANVAS.do_adjust_action start!========================== ", 33 );	
		the_layout_adjuster.do_adjust_action(root, this );
		Debug.println( "\n======================GRAPH_CANVAS.do_adjust_action Finish !========================== ", 33 );	
		
		double root_initial_aspect_ratio = root.get_position().find_aspect_ratio();
		the_initial_aspect_ratio = root_initial_aspect_ratio;
	
		repaint();
	}

	private void threshold_nodes(){
		Debug.println( "Threshold nodes: ",  22 );
		for ( Iterator _i = the_graph.find_nodes().iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();
			
				
			if ( the_graph.find_tree().depth(node) > DEFAULT_SFV_ORDER ){
				Debug.println( "\n" + node + " set_closed!", 22 );
				node.set_is_closed( true );
			}else{
				Debug.println( "\n" + node + " NOT set_closed!", 22 );
				node.set_is_closed( false );
			}		
		}

		Debug.println( "Threshold nodes finished! ", 22 );
	}

	private void create_initial_position_table(){
		ArrayList children_of_root	= Structure_Util.find_graph_root( the_graph ).get_children();

		HashMap initial_position_table	 = new HashMap();

		for (Iterator _i = children_of_root.iterator(); _i.hasNext(); ){
			Node a_child	= (Node)_i.next();

			Node_Position		np		= new Node_Position( a_child.get_position() );
			String			label	 	= a_child.get_label();

			initial_position_table.put( label, np );
		}

		set_initial_position_table( initial_position_table );

		Debug.println( "initial_position_table \n\t" + get_initial_position_table(), 11 );
	}
	
	private  void restore_initial_position(){		
		ArrayList children_of_root	= Structure_Util.find_graph_root( the_graph ).get_children();
		

		for (Iterator _i = children_of_root.iterator(); _i.hasNext(); ){
			Node a_child	= (Node)_i.next();

			String label	= a_child.get_label();

			Node_Position np	= (Node_Position)get_initial_position_table().get( label );

			a_child.set_position( new Node_Position( np ) );
		}


		Debug.println( "restore_initial_position:\t" + children_of_root, 11 );
	}

	private void do_current_drawing_experiment( long operation_time ){
		String experiment_reuslt_file = Experiment.dir 
							//+ File_Util.get_file_name( get_data_file_name() ) 
							//+"_" + the_layout_adjuster.get_name() 							
							+ "data_export.exp";
		try{
			Experiment_Metrics em = new Experiment_Metrics( this, operation_time, the_abridgement );
			String experiment_result	 = em.to_dynamic_string();

			Out.println( experiment_result );	

			Debug.println( "experiment_reuslt: \t"  + experiment_result , 57 );	
			File_Util.append_to_file( experiment_reuslt_file, experiment_result );	
			
			generate_image();
		}catch (IOException ioe){}	
	}

	
	//@return focus_file name
	private void generate_focus_trace(){	
		reset();

		ArrayList focus_trace	= new ArrayList();
		
		String[] adjusters	= Cluttered_Nodes_Adjuster.ADJUSTER_VARIANTS;
		
		String an_adjuster	= adjusters[0];
		set_layout_adjuster( new Layout_Factory().create_adjuster( an_adjuster ) );

		//NON_ADJUSTER is not the first one	
		//Out.println( "Do experiment by " + an_adjuster + ", data_file_name: "+ File_Util.get_file_name( data_file_name ) );
		Debug.println( "Node Positions are: " + the_graph.find_nodes(), 43 );

		for ( int j = 0; j < INTERACTION_TIMES ; j++ ){
			Node picked_focus;

			picked_focus	= Experiment_Util.pick_a_focus( this );

			if ( picked_focus == null ){
				reset();
				j--;
				continue;
			}else{
				focus_trace.add( picked_focus );
			}
			
			Debug.println( "picked_focus " + picked_focus, 45 );

			mouse_press_action( picked_focus );

			repaint();
		}//end of for j
		
		 this.focus_trace_file	= Experiment.experiment_focus_trace + "_" + File_Util.get_file_name( data_file_name ) + "_" +  System.currentTimeMillis();

		try{
			for ( Iterator _i = focus_trace.iterator(); _i.hasNext(); ){
				Node a_focus	= (Node)_i.next();

				File_Util.append_to_file( this.focus_trace_file, a_focus.get_label() );
			}
		}catch ( IOException ioe ){}		
	}
	
	private void do_experiment_dynamic_impl( ArrayList focus_trace ){
		File_Util.delete_file( Experiment_Util.generate_result_file_name(this) );

		String[] adjusters	= Cluttered_Nodes_Adjuster.ADJUSTER_VARIANTS;
		
		for ( int i = 0; i < adjusters.length; i++ ){
			reset();

					   
			String an_adjuster	= adjusters[i];
			if ( !an_adjuster.equals( Cluttered_Nodes_Adjuster.NON_ADJUSTER) ){
				Out.println( "Do experiment by " + an_adjuster );
				set_layout_adjuster( new Layout_Factory().create_adjuster( an_adjuster ) );						
				
				draw_initial_view();
				//generate_image();
				
				try{
					    for ( Iterator _i = focus_trace.iterator(); _i.hasNext(); ){
						String an_focus	= (String)_i.next();
						
						if ( an_focus.length() > 0 ){
							Debug.println( "do_experiment with focus: " + an_focus, 57 );

							long start_time	= System.currentTimeMillis();
							mouse_press_action( the_graph.search( an_focus ) );							
							long end_time	= System.currentTimeMillis();

							long operation_time	= end_time - start_time;

							do_current_drawing_experiment( operation_time );
						}
					}
				}
				catch (Exception e ){
					Out.println( "do_experiment_dynamic_impl Exception: " + e   );
					break;
				}
				
			}
			
		}
	}


	
	
	private void open_focus(){
		Node focus	= this. the_canvas_metrics.get_focus();		

		long	operation_time	= -1;
		if ( IS_ANIMATION ){
			new Layout_Actor( this ).open_node_animation( focus );			
		}else {
			new Layout_Actor( this ).open_node( focus );
			/*
			* for static experiment
			Debug.println( "\n======================GRAPH_CANVAS.do_adjust_action start!========================== ", 43 );	

			long start_time	= System.currentTimeMillis();
			the_layout_adjuster.do_adjust_action( focus, this );
			long end_time	= System.currentTimeMillis();

			operation_time	= end_time - start_time;
			Debug.println( "\n======================GRAPH_CANVAS.do_adjust_action Finish !========================== ", 43 );	

			Experiment_Metrics em = new Experiment_Metrics( this, the_initial_aspect_ratio,  operation_time, the_abridgement );

			try{
				String dynamic_experiment_result	 = em.to_dynamic_string();

				Out.println( dynamic_experiment_result );		
				if (is_doing_experiment){
					String experiment_reuslt_file	= File_Util.get_file_name( this.focus_trace_file )  + "_" + the_layout_adjuster.get_name();
					Debug.println( "experiment_reuslt_file"  + experiment_reuslt_file , 45 );	

					File_Util.append_to_file( experiment_reuslt_file, dynamic_experiment_result );	
				}

			}catch (IOException ioe){}		

			update_abridgment();
			Debug.println( "the_abridgement is updated: " + the_abridgement, 41 );
			*/
		}	
	}
	
	private void distort_leaf_clusters( Graph a_graph, Node focus_node ){
		Node_Distortor n_d = new Node_Distortor( this );

		n_d.distort_leaf_clusters( a_graph, focus_node, DEFAULT_SFV_ORDER, this.IS_ANIMATION );

		if ( this.the_canvas_metrics.get_focus() != null ){//interacted
			Animator_Util.pause( 2 );
		}
	}

	private void do_clustering(){	
		
		if ( IS_ANIMATION ){
			the_graph.do_pre_clustering_animation( the_canvas_metrics.find_thresholding(), this );	
		}	
		
		//the_graph.do_pre_clustering_animation( the_canvas_metrics.find_thresholding(), this );	
		if ( this.the_canvas_metrics.get_focus() != null ){//interacted
			Animator_Util.pause( 2 );
		}
		generate_image();
		the_graph.do_cluster( the_canvas_metrics.find_thresholding(), this );		
		if ( this.the_canvas_metrics.get_focus() != null ){//interacted
			Animator_Util.pause( 2 );
		}
		
		generate_image();
		//Debug.println( "GRAPH_CANVAS.do_post_cluster_animation on graph_canvas...", 3 );
		the_graph.do_post_clustering_animation( the_canvas_metrics.find_thresholding(), this );

		Animator_Util.pause( 2 );
		
	}

	private boolean has_pick_node( Node a_node, MouseEvent e ){		
		Node_Position np	= a_node.get_position();

		//Debug.println( "Click at: " + e.getX() + "," + e.getY(), 12 );
		//Debug.println( "The checked node " + a_node, 3 );
		return np.contains( e.getX(), e.getY() );		
	}

	private void update_abridgment(){
		this.the_abridgement	= new Abridgement( get_graph() );
	}

	//======================================
	//draw graph
	//======================================	
	 private void draw_graph( final Graphics g ){
		draw_root( g, the_canvas_metrics );

		draw_clusters( g, the_canvas_metrics );	
		draw_edges( g, the_canvas_metrics );
		draw_nodes( g, the_canvas_metrics );			
	}	

	private void draw_edges( final Graphics g, final Canvas_Metrics cm ){
		Edge_Drawer edge_drawer = new Edge_Drawer();

		ArrayList edges = Structure_Util.find_displayed_edges( the_graph );
		
		Debug.println( "find_displayed_edges : " + edges, 22 );
		for ( Iterator _i = edges.iterator() ; _i.hasNext() ; ){
			String[] edge	= (String[])_i.next();
			
			Node	
				start	= the_graph.search( edge[0] ),
				end	= the_graph.search( edge[1] );

			Edge	an_edge	 = new Edge( start, end );
			
			if ( HAS_IMPORTANT_EDGE ){
				if ( is_important_edge( an_edge) ){
					an_edge.set_is_important( true );
				}else{
					an_edge.set_is_important( false );
				}
			}			
			
			Debug.println( "Draw edge: " + an_edge, 22 );

			edge_drawer.draw( an_edge , g, cm.find_thresholding() );

		}

	}

	private boolean is_important_edge( Edge an_edge ){
		
		
		Node 
			start_node	= an_edge.get_start_node(),
			end_node	= an_edge.get_end_node();
		
		String
			start_parent_label	= start_node.get_parent().get_label(),
			end_parent_label	= end_node.get_parent().get_label(),
			focus_label		= this.the_canvas_metrics.get_focus().get_label();

		/*
		// this part is for citation: an important edge is an edge connecting to a newly displayed cluster.
		
		if ( start_parent_label.equals(focus_label) || end_parent_label.equals( focus_label ) ){
			return true;
		}else{
			return false;
		}*/

			
		// this part is for software architecture: an important edge is an internal edge.	
		if ( start_parent_label.equals( end_parent_label ) ){
			return true;
		}else{
			return false;
		}
	}
	
	//draw leaf clusters
	private void draw_nodes( final Graphics g, final Canvas_Metrics cm ){
		Debug.println( "Draw node: ", 22 );
		ArrayList leaf_clusters = the_graph.find_leaf_clusters();			

		Debug.println( "\tleaf_clusters\n\t" + leaf_clusters, 22 );
		for (Iterator _i = leaf_clusters.iterator(); _i.hasNext() ; ){
			Node a_node	= (Node)_i.next();
			Debug.println( "\t" + a_node, 21 );
			Node_Drawer node_drawer = new Node_Drawer_Factory().create_node_drawer( a_node, cm.get_focus() );	

			node_drawer.draw( a_node, g, cm.find_thresholding() );
		}//end of for Iterator
			
	}

	private void draw_clusters( final Graphics g, final Canvas_Metrics cm ){
		ArrayList cluster_nodes	= Structure_Util.find_opened_clusters(  the_graph );
		//Debug.println( "Draw draw_clusters: \n" + cluster_nodes, 17 );


		for ( Iterator _i = cluster_nodes.iterator(); _i.hasNext() ; ){
			Node a_cluster_node	 = (Node)_i.next();
			Node_Drawer node_drawer = new Node_Drawer_Factory().create_node_drawer( a_cluster_node, cm.get_focus() );	
			
			if ( !a_cluster_node.is_leaf() ){
				//Debug.println( "Is Not leaf cluster.", 3 );
				//Node_Position_Helper.set_cluster_node_position( a_cluster_node, this );
			}

			node_drawer.draw( a_cluster_node, g, cm.find_thresholding() );				
		}	
	}

	private void draw_root( final Graphics g, final Canvas_Metrics cm ){
		Node root	 = the_graph.find_tree().get_root();

		Node_Drawer node_drawer = new Node_Drawer_Factory().create_node_drawer( root, cm.get_focus() );	
	
		node_drawer.draw( root, g, cm.find_thresholding() );				
	}

	//======================================
	//	utils
	//======================================	
	private boolean is_focus( Node a_node ){
		if (  the_canvas_metrics.get_focus() == null){
			return false;
		}

		if ( a_node.get_label().equals(this. the_canvas_metrics.get_focus().get_label() ) ){
			return true;
		}

		return false;
	}
	
	private void add_focus( Node focus ){		
		if ( focus_history.contains(focus) ){
			focus_history.remove( focus );
		}

		focus_history.add( 0, focus );		
		
		the_canvas_metrics.set_focus( focus );
	}	
}
